// SKNotes - a note taking and gathering tool
// Copyright (C) 2009  Stefan Kueng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace SKNotes.Configuration
{
    using System;
    using System.Windows;
    using System.ComponentModel;
    using System.Diagnostics;

	public class WindowSettings : ApplicationSettings
	{
		public static readonly DependencyProperty SettingsProperty = 
			DependencyProperty.RegisterAttached(
				"Settings",
				typeof( string ),
				typeof( WindowSettings ),
				new FrameworkPropertyMetadata( new PropertyChangedCallback( OnWindowSettingsChanged ) ) );

		public static readonly DependencyProperty CollectedSettingProperty =
			DependencyProperty.RegisterAttached(
				"CollectedSetting",
				typeof( DependencyProperty ),
				typeof( WindowSettings ),
				new FrameworkPropertyMetadata( new PropertyChangedCallback( OnCollectedSettingChanged ) ) );

		public static readonly DependencyProperty ExcludeElementProperty = DependencyProperty.RegisterAttached(
			"ExcludeElement",
			typeof( bool ),
			typeof( WindowSettings ) );

		public WindowSettings( Window window ) :
			this( window, window.GetType().Name )
		{
		}

		public WindowSettings( Window window, string settingsKey ) :
			base( settingsKey )
		{
			if ( window == null )
			{
				throw new ArgumentNullException( "window" );
			}

			this.window = window;
			UseLocation = true;
			UseSize = true;
			UseWindowState = true;
			SaveOnClose = true;

			// settings 
			this.topSetting = CreateSetting( "Window.Top", Window.TopProperty );
			this.leftSetting = CreateSetting( "Window.Left", Window.LeftProperty );
			this.widthSetting = CreateSetting( "Window.Width", FrameworkElement.WidthProperty );
			this.heightSetting = CreateSetting( "Window.Height", FrameworkElement.HeightProperty );
			this.stateSetting = CreateSetting( "Window.WindowState", Window.WindowStateProperty );

			// subscribe to parent window's events
			this.window.Initialized += new EventHandler( WindowInitialized );
			this.window.Loaded += new RoutedEventHandler( WindowLoaded );
			this.window.Closing += new CancelEventHandler( WindowClosing );
		}

		public Window Window
		{
			get { return this.window; }
		}

		public ISetting TopSetting
		{
			get { return this.topSetting; }
		}

		public ISetting LeftSetting
		{
			get { return this.leftSetting; }
		} 

		public ISetting WidthSetting
		{
			get { return this.widthSetting; }
		}

		public ISetting HeightSetting
		{
			get { return this.heightSetting; }
		}

		public ISetting StateSetting
		{
			get { return this.stateSetting; }
		}

		public bool? SaveCondition
		{
			get { return this.saveCondition; }
			set { this.saveCondition = value; }
		}

		public bool UseLocation { get; set; }

		public bool UseSize { get; set; }

		public bool UseWindowState { get; set; }

		public bool AllowMinimized { get; set; }

		public bool SaveOnClose
		{
			get { return this.saveOnClose; }
			set { this.saveOnClose = value; }
		}

		public static string GetSettings( DependencyObject dependencyObject )
		{
			return dependencyObject.GetValue( SettingsProperty ) as string;
		}

		public static void SetSettings( DependencyObject dependencyObject, string settingsKey )
		{
			dependencyObject.SetValue( SettingsProperty, settingsKey );
		}

		public static DependencyProperty GetCollectedSetting( DependencyObject dependencyObject )
		{
			return dependencyObject.GetValue( SettingsProperty ) as DependencyProperty;
		}

		public static void SetCollectedSetting( DependencyObject dependencyObject, DependencyProperty dependencyProperty )
		{
			dependencyObject.SetValue( SettingsProperty, dependencyProperty );
		} 

		public static bool GetExcludeElement( DependencyObject obj )
		{
			return (bool)obj.GetValue( ExcludeElementProperty );
		}

		public static void SetExcludeElement( DependencyObject obj, bool exclude )
		{
			obj.SetValue( ExcludeElementProperty, exclude );
		}

		public override void Save()
		{
			if ( this.saveCondition.HasValue && this.saveCondition != this.window.DialogResult )
			{
				return;
			}
			base.Save();
		}

		protected override void OnCollectingSetting( SettingCollectorCancelEventArgs e )
		{
			FrameworkElement frameworkElement = e.Element as FrameworkElement;
			if ( frameworkElement == null )
			{
				e.Cancel = true;
				return;
			}

			// exclusion
			object exclude = frameworkElement.ReadLocalValue( ExcludeElementProperty );
			if ( exclude != null && exclude.GetType() == typeof( bool ) && (bool)exclude )
			{
				e.Cancel = true;
				return;
			}

			base.OnCollectingSetting( e );
		}

		private void WindowInitialized( object sender, EventArgs e )
		{
			if ( UseLocation )
			{
				Settings.Add( this.topSetting );
				Settings.Add( this.leftSetting );
			}
			if ( UseSize )
			{
				Settings.Add( this.widthSetting );
				Settings.Add( this.heightSetting );
			}
			if ( UseWindowState )
			{
				Settings.Add( this.stateSetting );
			}
			Load();
		}

		private void WindowLoaded( object sender, RoutedEventArgs e )
		{
			Load();
		} 

		private void WindowClosing( object sender, CancelEventArgs e )
		{
			if ( this.saveOnClose == false )
			{
				return;
			}
			Save();
		} 

		private DependencyPropertySetting CreateSetting( string name, DependencyProperty dependencyProperty )
		{
			DependencyPropertySetting propertySetting = new DependencyPropertySetting( name, this.window, dependencyProperty );
			propertySetting.ValueSaving += new SettingValueCancelEventHandler( ValueSaving );
			return propertySetting;
		}

		private void ValueSaving( object sender, SettingValueCancelEventArgs e )
		{
			if ( AllowMinimized == false && this.window.WindowState == WindowState.Minimized )
			{
				e.Cancel = true;
			}
		}

		private static void OnWindowSettingsChanged( DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e )
		{
			Window window = dependencyObject as Window;
			if ( window == null )
			{
				Debug.WriteLine( "WindowSettings: invalid window" );
				return;
			}

			if ( window.GetValue( DependencyPropertySetting.ApplicationSettingsProperty ) != null )
			{
				return; // window contains already an application setting
			}

			string settingsKey = e.NewValue as string;
			if ( string.IsNullOrEmpty( settingsKey ) )
			{
				Debug.WriteLine( "WindowSettings: missing window settings key" );
				return;
			}

			// create and attach the application settings to the window
			WindowSettings windowSettings = new WindowSettings( window, settingsKey );
			window.SetValue( DependencyPropertySetting.ApplicationSettingsProperty, windowSettings );
		} 

		private static void OnCollectedSettingChanged( DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e )
		{
			FrameworkElement frameworkElement = dependencyObject as FrameworkElement;
			if ( frameworkElement == null )
			{
				Debug.WriteLine( "WindowSettings: invalid framework element" );
				return;
			}

			DependencyProperty dependencyProperty = e.NewValue as DependencyProperty;
			if ( dependencyProperty == null )
			{
				Debug.WriteLine( "WindowSettings: missing dependency property" );
				return;
			}

			// search the window settings
			WindowSettings windowSettings =
					frameworkElement.ReadLocalValue( DependencyPropertySetting.ApplicationSettingsProperty ) as WindowSettings;
			if ( windowSettings == null )
			{
				Debug.WriteLine( "WindowSettings: missing window settings in element " + frameworkElement );
				return;
			}

			DependencyPropertySettingCollector collector = 
				new DependencyPropertySettingCollector( frameworkElement, dependencyProperty );
			windowSettings.SettingCollectors.Add( collector );
		} 

		private readonly Window window;
		private readonly DependencyPropertySetting topSetting;
		private readonly DependencyPropertySetting leftSetting;
		private readonly DependencyPropertySetting widthSetting;
		private readonly DependencyPropertySetting heightSetting;
		private readonly DependencyPropertySetting stateSetting;
		private bool? saveCondition;
		private bool saveOnClose;

	}
} 
